﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;

namespace NewKataminoSolver
{
    class Penta : IFormattable
    {
        public string ToString(string s, IFormatProvider ifp)
        {
            // statisfy IFormattable
            StringBuilder str = new StringBuilder(96);
            for (int i = 0; i < x.Length; i++)
                str.Append("({0},{1})", x[i], y[i]);
            return str.ToString();
        }
        public static bool TryParse(string s, out Penta result)
        { // postponed. After serialization and string reading
            result = new Penta();
            for (int i = 0; i < 5; i++)
            {
                
            }
            return true;
        }
 
        public int[] x;
        public int[] y;
        public Penta[] variations;
        public Rectangle box;

        public Penta()
        {
            x = new int[5];
            y = new int[5];
            variations = new Penta[0];
        }
        public Penta(int[] _x, int[] _y)
        {
            x = _x;
            y = _y;
        }
        public Penta Norm() // minimize such that all x,y >= 0 but minimal
        {
            this.Sort();

            int minx = this.x.Min();
            int miny = this.y.Min();

            for (int i = 0; i < 5; i++)
            {
                this.x[i] = this.x[i] - minx;
                this.y[i] = this.y[i] - miny;
            }
            box = new Rectangle(0, 0, this.x.Max(), this.y.Max());
            return this;
        }
        public Penta UL() // make upper left point 0,0
        {
            this.Norm();
            this.box.Offset(-this.x[0], -this.y[0]);
            for (int i = 4; i >= 0; i--)
            {
                this.x[i] = this.x[i] - this.x[0];
                this.y[i] = this.y[i] - this.y[0];
            }
            return this;
        }
        public Rectangle Rect()
        {
            return this.box;
        }
        public Penta Rotate()
        {
            Penta result = new Penta();

            int maxx = x.Max();
            int miny = y.Min();

            for (int i = 0; i < 5; i++)
            {
                result.x[i] = this.y[i] - miny;
                result.y[i] = maxx - this.x[i];
            }

            return result;
        }
        public Penta Mirror()
        {
            Penta result = new Penta();

            int minx = x.Min();
            int maxy = y.Max();

            for (int i = 0; i < 5; i++)
            {
                result.x[i] = this.x[i] - minx;
                result.y[i] = maxy - this.y[i];
            }

            return result;
        }
        public Penta Sort()
        {
            for (int i = 0; i < 4; i++)
                for (int j = i + 1; j < 5; j++)
                {
                    if ((y[i] > y[j]) || ((y[i] == y[j]) && (x[i] > x[j])))
                    {   // swap
                        int tmp = x[i];
                        x[i] = x[j];
                        x[j] = tmp;
                        tmp = y[i];
                        y[i] = y[j];
                        y[j] = tmp;
                    }
                }
            return this;
        }
        public Penta debug()
        {
            Debug.WriteLine("");
            for (int i = 0; i < 5; i++)
            {
                Debug.WriteLine(x[i].ToString(CultureInfo.CurrentCulture) + "," + y[i].ToString(CultureInfo.CurrentCulture));
            }
            return this;
        }
        public void Tester()
        {
            int[] x0 = { 6, 4, 5, 3, 7 };
            int[] y0 = { 2, 2, 2, 2, 2 };

            Penta t = new Penta(x0, y0).Norm();
            t.debug();
            t.Mirror().debug();
            t.Rotate().Mirror().debug();
            t.Rotate().Rotate().Sort().debug();
            Debug.WriteLine("=========================");
            this.Variations();
            // this.Draw();
        }
        public Penta[] Variations(int[] _x, int[] _y, int r, int m)
        {
            Penta[] result = new Penta[r * m];
            Penta p = new Penta(_x, _y);
            int j = 0;

            for (int _r = 0; _r < r; _r++)
            {
                result[j++] = p.UL().debug();
                if (m > 1)
                {
                    result[j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }
            variations = result;
            return result;
        }
        public Penta[][] Variations()
        {
            Penta[][] result = new Penta[12][];

            int i = 0;

            int[] y0 = { 0, 1, 2, 3, 4 };
            int[] x0 = { 0, 0, 0, 0, 0 };
            Penta p = new Penta(x0, y0);
            int rotates = 2;
            int mirrors = 1;

            result[i] = new Penta[rotates * mirrors];

            result[i][0] = p.Norm().debug();
            result[i][1] = p.Rotate().UL().debug();

            i = 1;
            int[] x1 = { 0, 1, 2, 3, 0 };
            int[] y1 = { 0, 0, 0, 0, 1 };
            p = new Penta(x1, y1);

            rotates = 4;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            int j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.Norm().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 2;
            int[] x2 = { 0, 1, 2, 3, 1 };
            int[] y2 = { 0, 0, 0, 0, 1 };
            p = new Penta(x2, y2);

            rotates = 4;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 3;
            int[] x3 = { 0, 1, 2, 2, 3 };
            int[] y3 = { 0, 0, 0, 1, 1 };
            p = new Penta(x3, y3);

            rotates = 4;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 4;
            int[] x4 = { 0, 1, 2, 0, 0 };
            int[] y4 = { 0, 0, 0, 1, 2 };
            p = new Penta(x4, y4);

            rotates = 4;
            mirrors = 1;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 5;
            int[] x5 = { 0, 1, 2, 0, 1 };
            int[] y5 = { 0, 0, 0, 1, 1 };
            p = new Penta(x5, y5);

            rotates = 4;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 6;
            int[] x6 = { 0, 1, 2, 0, 2 };
            int[] y6 = { 0, 0, 0, 1, 1 };
            p = new Penta(x6, y6);

            rotates = 4;
            mirrors = 1;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 7;
            int[] x7 = { 0, 1, 2, 0, 2 };
            int[] y7 = { 1, 1, 1, 2, 0 };
            p = new Penta(x7, y7);

            rotates = 2;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 8;
            int[] x8 = { 1, 2, 0, 1, 0 };
            int[] y8 = { 0, 0, 1, 1, 2 };
            p = new Penta(x8, y8);

            rotates = 4;
            mirrors = 1;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 9;
            int[] x9 = { 1, 2, 0, 1, 1 };
            int[] y9 = { 0, 0, 1, 1, 2 };
            p = new Penta(x9, y9);

            rotates = 4;
            mirrors = 2;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 10;
            int[] x10 = { 1, 0, 1, 2, 1 };
            int[] y10 = { 0, 1, 1, 1, 2 };
            p = new Penta(x10, y10);

            rotates = 1;
            mirrors = 1;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            i = 11;
            int[] x11 = { 0, 1, 2, 1, 1 };
            int[] y11 = { 0, 0, 0, 1, 2 };
            p = new Penta(x11, y11);

            rotates = 4;
            mirrors = 1;
            result[i] = new Penta[rotates * mirrors];
            j = 0;
            for (int r = 0; r < rotates; r++)
            {
                result[i][j++] = p.UL().debug();
                if (mirrors > 1)
                {
                    result[i][j++] = p.Mirror().UL().debug();
                }
                p = p.Rotate();
            }

            // Variations(x0, y0, 2, 1);

            return result;
        }
    }
}
